package org.fujene.memoryblock.pool;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.List;

import org.fujene.constants.Constants;

public class MemoryBlockPool{
	private List<byte[]> MEMORYBLOCKS;
	private volatile BitSet usedList;
	private int blockAmount;
	private BitSet lockedTypes;
	private Object depositLock;

	public MemoryBlockPool(int blockAmount, int blockSize){
		ArrayList<byte[]> tempList = new ArrayList<byte[]>(blockAmount);
		for(int i = 0; i < blockAmount; i++){
			tempList.add(new byte[blockSize]);
			Arrays.fill(tempList.get(i), (byte)0xff);
		}
		MEMORYBLOCKS = Collections.unmodifiableList(tempList);
		this.usedList = new BitSet(blockAmount);
		this.lockedTypes = new BitSet(Constants.MEMORY_TYPE_KINDS);
		this.blockAmount = blockAmount;
		this.depositLock = new Object();
	}

	public byte[] withdraw(int type){
		int blockIndex = usedList.nextClearBit(0);
		if(blockIndex >= blockAmount){
			// No block available, wait until a block releases
			try{
				if(lockedTypes.get(type)) return null;
				lockedTypes.set(type);
				synchronized(depositLock){
					try{
						depositLock.wait();
					}catch(InterruptedException e){}
				}
			}finally{
				lockedTypes.clear(type);
			}
		}
		usedList.set(blockIndex);
		return MEMORYBLOCKS.get(blockIndex);
	}

	public void deposit(byte[] block){
		int index;
		if((index = MEMORYBLOCKS.indexOf(block)) != -1){
			Arrays.fill(block, (byte)0xff);
			usedList.clear(index);
			synchronized(depositLock){
				depositLock.notify();
			}
		}
	}
}
